home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / profiler.js < prev    next >
Text File  |  2010-01-15  |  14KB  |  416 lines

  1. /* See license.txt for terms of usage */
  2.  
  3.  
  4. FBL.ns(function() { with (FBL) {
  5.  
  6. var toggleProfiling = $("fbToggleProfiling");
  7.  
  8. // ************************************************************************************************
  9.  
  10. Firebug.Profiler = extend(Firebug.Module,
  11. {
  12.     dispatchName: "profiler",
  13.     showContext: function(browser, context)
  14.     {
  15.         this.setEnabled(context);
  16.     },
  17.  
  18.     onPanelEnable: function(panelName)
  19.     {
  20.         if (panelName == "net" || panelName == "script")
  21.             this.setEnabled();
  22.     },
  23.  
  24.     onPanelDisable: function(panelName)
  25.     {
  26.        if (panelName == "net" || panelName == "script")
  27.             this.setEnabled();
  28.     },
  29.  
  30.     setEnabled: function()
  31.     {
  32.         // The profiler is available only if the debugger (script panel) and console are enabled.
  33.         var debuggerEnabled = Firebug.Debugger.isAlwaysEnabled();
  34.         var consoleEnabled = Firebug.Console.isAlwaysEnabled();
  35.         toggleProfiling.disabled = !debuggerEnabled || !consoleEnabled;
  36.  
  37.         // Update button's tooltip.
  38.         var tooltipText = toggleProfiling.disabled ? $STR("ProfileButton.Disabled.Tooltip")
  39.             : $STR("ProfileButton.Enabled.Tooltip");
  40.         toggleProfiling.setAttribute("tooltiptext", tooltipText);
  41.     },
  42.  
  43.     toggleProfiling: function(context)
  44.     {
  45.         if (fbs.profiling)
  46.             this.stopProfiling(context);
  47.         else
  48.             this.startProfiling(context);
  49.     },
  50.  
  51.     startProfiling: function(context, title)
  52.     {
  53.         fbs.startProfiling();
  54.  
  55.         Firebug.chrome.setGlobalAttribute("cmd_toggleProfiling", "checked", "true");
  56.  
  57.         var isCustomMessage = !!title;
  58.         if (!isCustomMessage)
  59.             title = $STR("ProfilerStarted");
  60.  
  61.         context.profileRow = this.logProfileRow(context, title);
  62.         context.profileRow.customMessage = isCustomMessage ;
  63.     },
  64.  
  65.     isProfiling: function()
  66.     {
  67.         return (Firebug.chrome.getGlobalAttribute("cmd_toggleProfiling", "checked") === "true")
  68.     },
  69.  
  70.     stopProfiling: function(context, cancelReport)
  71.     {
  72.         var totalTime = fbs.stopProfiling();
  73.         if (totalTime == -1)
  74.             return;
  75.  
  76.         Firebug.chrome.setGlobalAttribute("cmd_toggleProfiling", "checked", "false");
  77.  
  78.         if (cancelReport)
  79.             delete context.profileRow;
  80.         else
  81.             this.logProfileReport(context)
  82.     },
  83.  
  84.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  85.  
  86.     logProfileRow: function(context, title)
  87.     {
  88.         var row = Firebug.Console.openGroup(title, context, "profile",
  89.             Firebug.Profiler.ProfileCaption, true, null, true);
  90.         setClass(row, "profilerRunning");
  91.  
  92.         Firebug.Console.closeGroup(context, true);
  93.  
  94.         return row;
  95.     },
  96.  
  97.     logProfileReport: function(context)
  98.     {
  99.         var calls = [];
  100.         var totalCalls = 0;
  101.         var totalTime = 0;
  102.  
  103.         var sourceFileMap = context.sourceFileMap;
  104.         jsd.enumerateScripts({enumerateScript: function(script)
  105.         {
  106.             if (script.callCount)
  107.             {
  108.                 if (!Firebug.filterSystemURLs || !isSystemURL(script.fileName))
  109.                 {
  110.                     var sourceLink = FBL.getSourceLinkForScript(script, context);
  111.                     if (sourceLink && sourceLink.href in sourceFileMap)
  112.                     {
  113.                         var call = new ProfileCall(script, context, script.callCount, script.totalExecutionTime,
  114.                                 script.totalOwnExecutionTime, script.minExecutionTime, script.maxExecutionTime, sourceLink);
  115.                         calls.push(call);
  116.  
  117.                         totalCalls += script.callCount;
  118.                         totalTime += script.totalOwnExecutionTime;
  119.                     }
  120.                 }
  121.                 script.clearProfileData();
  122.             }
  123.         }});
  124.  
  125.         for (var i = 0; i < calls.length; ++i)
  126.             calls[i].percent = Math.round((calls[i].totalOwnTime/totalTime) * 100 * 100) / 100;
  127.  
  128.         calls.sort(function(a, b)
  129.         {
  130.            return a.totalOwnTime < b.totalOwnTime ? 1 : -1;
  131.         });
  132.  
  133.         totalTime = Math.round(totalTime * 1000) / 1000;
  134.  
  135.         var groupRow = context.profileRow && context.profileRow.ownerDocument
  136.             ? context.profileRow
  137.             : this.logProfileRow(context, "");
  138.         delete context.profileRow;
  139.  
  140.         removeClass(groupRow, "profilerRunning");
  141.  
  142.         if (totalCalls > 0)
  143.         {
  144.             var captionBox = groupRow.getElementsByClassName("profileCaption").item(0);
  145.             if (!groupRow.customMessage)
  146.                 captionBox.textContent = $STR("Profile");
  147.             var timeBox = groupRow.getElementsByClassName("profileTime").item(0);
  148.             timeBox.textContent = $STRP("plural.Profile_Time", [totalTime, totalCalls], 1);
  149.  
  150.             var groupBody = groupRow.lastChild;
  151.             var sizer = Firebug.Profiler.ProfileTable.tag.replace({}, groupBody);
  152.             var table = sizer.firstChild;
  153.             var tHeader = table.lastChild;  // no rows inserted.
  154.  
  155.             var tag = Firebug.Profiler.ProfileCall.tag;
  156.             var insert = tag.insertRows;
  157.  
  158.             for (var i = 0; i < calls.length; ++i) {
  159.                 calls[i].index = i;
  160.                 context.throttle(insert, tag, [{object: calls[i]}, tHeader]);
  161.             }
  162.  
  163.             context.throttle(groupRow.scrollIntoView, groupRow);
  164.         }
  165.         else
  166.         {
  167.             var captionBox = groupRow.getElementsByClassName("profileCaption").item(0);
  168.             captionBox.textContent = $STR("NothingToProfile");
  169.         }
  170.     }
  171. });
  172.  
  173. // ************************************************************************************************
  174.  
  175. Firebug.Profiler.ProfileTable = domplate(
  176. {
  177.     tag:
  178.       DIV({class: "profileSizer", "tabindex": "-1" },
  179.         TABLE({class: "profileTable", cellspacing: 0, cellpadding: 0, width: "100%", "role": "grid"},
  180.             TBODY({class: "profileTbody", "role": "presentation"},
  181.                 TR({class: "headerRow focusRow profileRow subFocusRow", onclick: "$onClick", "role": "row"},
  182.                     TH({class: "headerCell alphaValue a11yFocus", "role": "columnheader"},
  183.                         DIV({class: "headerCellBox"},
  184.                             $STR("Function")
  185.                         )
  186.                     ),
  187.                     TH({class: "headerCell a11yFocus" , "role": "columnheader"},
  188.                         DIV({class: "headerCellBox", title: $STR("CallsHeaderTooltip")},
  189.                             $STR("Calls")
  190.                         )
  191.                     ),
  192.                     TH({class: "headerCell headerSorted a11yFocus", "role": "columnheader", "aria-sort": "descending"},
  193.                         DIV({class: "headerCellBox", title: $STR("PercentTooltip")},
  194.                             $STR("Percent")
  195.                         )
  196.                     ),
  197.                     TH({class: "headerCell a11yFocus", "role": "columnheader"},
  198.                         DIV({class: "headerCellBox", title: $STR("OwnTimeHeaderTooltip")},
  199.                             $STR("OwnTime")
  200.                         )
  201.                     ),
  202.                     TH({class: "headerCell a11yFocus", "role": "columnheader"},
  203.                         DIV({class: "headerCellBox", title: $STR("TimeHeaderTooltip")},
  204.                             $STR("Time")
  205.                         )
  206.                     ),
  207.                     TH({class: "headerCell a11yFocus", "role": "columnheader"},
  208.                         DIV({class: "headerCellBox", title: $STR("AvgHeaderTooltip")},
  209.                             $STR("Avg")
  210.                         )
  211.                     ),
  212.                     TH({class: "headerCell a11yFocus", "role": "columnheader"},
  213.                         DIV({class: "headerCellBox", title: $STR("MinHeaderTooltip")},
  214.                             $STR("Min")
  215.                         )
  216.                     ),
  217.                     TH({class: "headerCell a11yFocus", "role": "columnheader"},
  218.                         DIV({class: "headerCellBox", title: $STR("MaxHeaderTooltip")},
  219.                             $STR("Max")
  220.                         )
  221.                     ),
  222.                     TH({class: "headerCell alphaValue a11yFocus", "role": "columnheader"},
  223.                         DIV({class: "headerCellBox"},
  224.                             $STR("File")
  225.                         )
  226.                     )
  227.                 )
  228.             )
  229.           )
  230.         ),
  231.  
  232.     onClick: function(event)
  233.     {
  234.         var table = getAncestorByClass(event.target, "profileTable");
  235.         var header = getAncestorByClass(event.target, "headerCell");
  236.         if (!header)
  237.             return;
  238.  
  239.         var numerical = !hasClass(header, "alphaValue");
  240.  
  241.         var colIndex = 0;
  242.         for (header = header.previousSibling; header; header = header.previousSibling)
  243.             ++colIndex;
  244.  
  245.         this.sort(table, colIndex, numerical);
  246.     },
  247.  
  248.     sort: function(table, colIndex, numerical)
  249.     {
  250.         var tbody = getChildByClass(table, "profileTbody");
  251.  
  252.         var values = [];
  253.         for (var row = tbody.childNodes[1]; row; row = row.nextSibling)
  254.         {
  255.             var cell = row.childNodes[colIndex];
  256.             var value = numerical ? parseFloat(cell.textContent) : cell.textContent;
  257.             values.push({row: row, value: value});
  258.         }
  259.  
  260.         values.sort(function(a, b) { return a.value < b.value ? -1 : 1; });
  261.  
  262.         var headerRow = tbody.firstChild;
  263.         var headerSorted = getChildByClass(headerRow, "headerSorted");
  264.         removeClass(headerSorted, "headerSorted");
  265.         if (headerSorted)
  266.             headerSorted.removeAttribute('aria-sort');
  267.  
  268.         var header = headerRow.childNodes[colIndex];
  269.         setClass(header, "headerSorted");
  270.  
  271.         if (!header.sorted || header.sorted == 1)
  272.         {
  273.             removeClass(header, "sortedDescending");
  274.             setClass(header, "sortedAscending");
  275.             header.setAttribute("aria-sort", "ascending");
  276.  
  277.             header.sorted = -1;
  278.  
  279.             for (var i = 0; i < values.length; ++i) {
  280.                 values[i].row.setAttribute("odd", (i % 2));
  281.                 tbody.appendChild(values[i].row);
  282.             }
  283.         }
  284.         else
  285.         {
  286.             removeClass(header, "sortedAscending");
  287.             setClass(header, "sortedDescending");
  288.             header.setAttribute("aria-sort", "descending")
  289.  
  290.             header.sorted = 1;
  291.  
  292.             for (var i = values.length-1; i >= 0; --i) {
  293.                 values[i].row.setAttribute("odd", (Math.abs(i-values.length-1) % 2));
  294.                 tbody.appendChild(values[i].row);
  295.             }
  296.         }
  297.     }
  298. });
  299.  
  300. // ************************************************************************************************
  301.  
  302. Firebug.Profiler.ProfileCaption = domplate(Firebug.Rep,
  303. {
  304.     tag:
  305.         SPAN({class: "profileTitle", "role": "status"},
  306.             SPAN({class: "profileCaption"}, "$objects"),
  307.             " ",
  308.             SPAN({class: "profileTime"}, "")
  309.         )
  310. });
  311.  
  312. // ************************************************************************************************
  313.  
  314. Firebug.Profiler.ProfileCall = domplate(Firebug.Rep,
  315. {
  316.     tag:
  317.         TR({"class": "focusRow profileRow subFocusRow", odd: "$object|isOddRow", "role": "row"},
  318.             TD({"class": "profileCell", "role": "presentation"},
  319.                 FirebugReps.OBJECTLINK("$object|getCallName")
  320.             ),
  321.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.callCount"),
  322.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.percent%"),
  323.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.totalOwnTime|roundTime\\ms"),
  324.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.totalTime|roundTime\\ms"),
  325.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object|avgTime|roundTime\\ms"),
  326.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.minTime|roundTime\\ms"),
  327.             TD({"class": "a11yFocus profileCell", "role": "gridcell"}, "$object.maxTime|roundTime\\ms"),
  328.             TD({class: "linkCell profileCell", "role": "presentation"},
  329.                 TAG(FirebugReps.SourceLink.tag, {object: "$object|getSourceLink"})
  330.             )
  331.         ),
  332.  
  333.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  334.  
  335.     isOddRow: function(call)
  336.     {
  337.         return (call.index % 2) ? 1 : 0;
  338.     },
  339.  
  340.     getCallName: function(call)
  341.     {
  342.         return cropString(getFunctionName(call.script, call.context), 60);
  343.     },
  344.  
  345.     avgTime: function(call)
  346.     {
  347.         return call.totalTime / call.callCount;
  348.     },
  349.  
  350.     getSourceLink: function(call)
  351.     {
  352.         return call.sourceLink;
  353.     },
  354.  
  355.     roundTime: function(ms)
  356.     {
  357.         return Math.round(ms * 1000) / 1000;
  358.     },
  359.  
  360.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  361.  
  362.     className: "profile",
  363.  
  364.     supportsObject: function(object)
  365.     {
  366.         return object instanceof ProfileCall;
  367.     },
  368.  
  369.     inspectObject: function(call, context)
  370.     {
  371.         var sourceLink = this.getSourceLink(call);
  372.         Firebug.chrome.select(sourceLink);
  373.     },
  374.  
  375.     getTooltip: function(call)
  376.     {
  377.         try
  378.         {
  379.             var fn = unwrapIValue(call.script.functionObject);
  380.             return FirebugReps.Func.getTooltip(fn, call.context);
  381.         }
  382.         catch (exc)
  383.         {
  384.         }
  385.     },
  386.  
  387.     getContextMenuItems: function(call, target, context)
  388.     {
  389.         var fn = unwrapIValue(call.script.functionObject);
  390.         return FirebugReps.Func.getContextMenuItems(fn, call.script, context);
  391.     }
  392. });
  393.  
  394. // ************************************************************************************************
  395.  
  396. function ProfileCall(script, context, callCount, totalTime, totalOwnTime, minTime, maxTime, sourceLink)
  397. {
  398.     this.script = script;
  399.     this.context = context;
  400.     this.callCount = callCount;
  401.     this.totalTime = totalTime;
  402.     this.totalOwnTime = totalOwnTime;
  403.     this.minTime = minTime;
  404.     this.maxTime = maxTime;
  405.     this.sourceLink = sourceLink;
  406. }
  407.  
  408. // ************************************************************************************************
  409.  
  410. Firebug.registerModule(Firebug.Profiler);
  411. Firebug.registerRep(Firebug.Profiler.ProfileCall);
  412.  
  413. // ************************************************************************************************
  414.  
  415. }});
  416.